using System.Collections.Generic;
using System.Linq;
using Godot;
using Godot.Collections;
using ZFramework;

namespace PaintAWorld.Gd
{
    public class MeshBuilder
    {
        private readonly List<Vector3> _vertices = [];
        private readonly List<int> _indices = [];
        private readonly List<Color> _colors = [];
        private readonly List<Vector3> _normals = [];

        public bool IsEmpty => _vertices.Count == 0;

        public void Build(ref Array arrays)
        {
            arrays.Resize((int)Mesh.ArrayType.Max);

            arrays[(int)Mesh.ArrayType.Vertex] = _vertices.ToArray();
            arrays[(int)Mesh.ArrayType.Index] = _indices.ToArray();
            arrays[(int)Mesh.ArrayType.Normal] = _normals.ToArray();
            arrays[(int)Mesh.ArrayType.Color] = _colors.ToArray();
        }

        public void AddQuad(Vector3 leftBottom, Vector3 right, Vector3 up, Vector3 normal, Color color)
        {
            var count = _vertices.Count;

            var leftTop = leftBottom + up;
            var rightTop = leftTop + right;
            var rightBottom = leftBottom + right;

            _vertices.AddRange([leftBottom, leftTop, rightTop, rightBottom]);
            _indices.AddRange([count, count + 1, count + 2, count, count + 2, count + 3]);
            _colors.AddRange(Enumerable.Repeat(color, 4));
            _normals.AddRange(Enumerable.Repeat(normal, 4));
        }

        public void AddQuad(Vector3 leftBottom, Vector3 right, Vector3 up, Color color)
        {
            AddQuad(leftBottom, right, up, right.Cross(up).Normalized(), color);
        }

        public void AddCube(Vector3 position, Vector3 offset, float voxelSize, Facing facing, Color color)
        {
            if (facing.HasFlag(Facing.East))
            {
                AddQuad(offset + voxelSize * (position +  Vector3.Right), voxelSize * Vector3.Up, voxelSize * Vector3.Back, Vector3.Right, color);
            }

            if (facing.HasFlag(Facing.Down))
            {
                AddQuad(offset + voxelSize * position, voxelSize * Vector3.Right, voxelSize * Vector3.Back, Vector3.Down, color);
            }

            if (facing.HasFlag(Facing.South))
            {
                AddQuad(offset + voxelSize * (position + Vector3.Back), voxelSize * Vector3.Right, voxelSize * Vector3.Up, Vector3.Back, color);
            }

            if (facing.HasFlag(Facing.West))
            {
                AddQuad(offset + voxelSize * position, voxelSize * Vector3.Back, voxelSize * Vector3.Up, Vector3.Left, color);
            }

            if (facing.HasFlag(Facing.Up))
            {
                AddQuad(offset + voxelSize * (position + Vector3.Up), voxelSize * Vector3.Back, voxelSize * Vector3.Right, Vector3.Up, color);
            }

            if (facing.HasFlag(Facing.North))
            {
                AddQuad(offset + voxelSize * position, voxelSize * Vector3.Up, voxelSize * Vector3.Right, Vector3.Forward, color);
            }
        }

        public void AddCube(int x, int y, int z, Vector3 offset, float voxelSize, Facing facing, Color color) => AddCube(new Vector3(x, y, z), offset, voxelSize, facing, color);
    }
}